import { nodeTypes } from './ast'


const enum tagType {
  START,
  END
}


export function baseParse(content: string) {
  const context = createContextParse(content)
  return createRoot(parseChildren(context, []))
}


function parseChildren(context: { source: string }, ancestors) {

  const nodes: any = []
  while (!isEnd(context, ancestors)) {
    let node
    let s = context.source
    if (s.startsWith("{{")) {
      node = parseInterpolation(context)
    } else if (s[0] === '<') {
      // element
      if (/[a-z]/i.test(s[1])) {
        node = parseElement(context, ancestors)
      }
    }

    if (!node) {
      // text
      node = parseText(context)
    }
    nodes.push(node)
  }
  return nodes
}

function isEnd(context, ancestors) {
  const s = context.source
  // 结束标签
  if (s.startsWith("</")) {
    for (let i = ancestors.length - 1; i >= 0; i--) {
      const tag = ancestors[i].tag
      if (startsWithEndTagOpen(s, tag)) {
        return true
      }
    }
  }
  // source 有值
  return !s
}




function parseText(context: { source: string }) {
  let endIndex = context.source.length
  const endTokens = ["<", "{{"]
  const s = context.source
  for (let i = 0; i < endTokens.length; i++) {
    const index = s.indexOf(endTokens[i])
    if (index !== -1 && endIndex > index) {
      endIndex = index
    }

  }

  const content = parseTextData(context, endIndex)

  return {
    type: nodeTypes.TEXT,
    content
  }
}


function parseTextData(context, length: number) {
  const content = context.source.slice(0, length)
  advanceBy(context, length)
  return content
}

function parseElement(context: { source: string }, ancestors) {
  // ancestors = [div,span,div]
  const element: any = parseTag(context, tagType.START)
  ancestors.push(element)
  element.children = parseChildren(context, ancestors)
  ancestors.pop()
  if (startsWithEndTagOpen(context.source, element.tag)) {
    parseTag(context, tagType.END)
  } else {
    throw new Error(`缺少结束标签:${element.tag}`)
  }
  return element
}


function startsWithEndTagOpen(source, tag) {
  return source.startsWith("</") && source.slice(2, 2 + tag.length).toLowerCase() === tag.toLowerCase()
}


function parseTag(context: { source: string }, type: tagType) {
  const match: any = /^<\/?([a-z]*)/i.exec(context.source)
  const tag = match[1]
  advanceBy(context, match[0].length)
  advanceBy(context, 1)
  if (type === tagType.END) return
  return {
    type: nodeTypes.ELEMENT,
    tag
  }
}


function parseInterpolation(context: { source: string }) {
  // {{message}}
  const openString = "{{"
  const closeString = "}}"
  const closeIndex = context.source.indexOf(closeString, closeString.length)
  advanceBy(context, openString.length)
  const rawConentLength = closeIndex - openString.length
  const rawContent = parseTextData(context, rawConentLength)
  const content = rawContent.trim()
  advanceBy(context, closeString.length)
  return {
    type: nodeTypes.INTERPOLATION,
    content: {
      type: nodeTypes.SIMPLE_EXPRESSION,
      content: content
    }
  }
}

function advanceBy(context, length: number) {
  context.source = context.source.slice(length)
}

function createRoot(children) {
  return {
    children
  }
}

function createContextParse(content) {
  return {
    source: content
  }
}